/*
 * REFEventReactor.cpp
 *
 *  Created on: Jun 18, 2013
 *      Author: Lucifer
 */

#include "../pch.h"
#include "events.h"
#include "REFEventReactor.h"
#include "REFTimerService.h"

namespace BFX
{

////////////////////////////////////////////////////////////////////////////////
// REFTimerQueueSet

/**
 * Adds a timer queue to the set.
 * @param pTimers
 */
void REFTimerQueueSet::Add(REFTimerQueue* pTimers)
{
}

/**
 * Rmeoves a timer queue from the set.
 * @param pTimers
*/
void REFTimerQueueSet::Remove(REFTimerQueue* pTimers)
{
}

/**
 * Determines whether all queues in the set are empty.
 * @returns True if empty; otherwise false.
 */
bool REFTimerQueueSet::IsEmpty() const
{
}

/**
 * Gets the wait interval in microseconds.
 * @param lMaxWaitDuration The max wait duration.
 * @returns The wait interval.
 */
long REFTimerQueueSet::GetWaitInterval(long lMaxWaitInterval) const
{
}

/**
 * Dequeues all ready timer contexts.
 * @param[out] pContexts The list uses to contain all ready contexts.
 */
void REFTimerQueueSet::DequeueReadyContexts(REFEventContextList* pContexts)
{
}

/**
 * Dequeues all timer contexts.
 * @param[out] pContexts The list uses to contain all contexts.
 */
void REFTimerQueueSet::DequeueAllContexts(REFEventContextList* pContexts)
{
}

////////////////////////////////////////////////////////////////////////////////
// REFEventReactor

/**
 * Initializes a new instance of BFX::REFEventReactor with specified event dispatcher.
 * @param pDispatcher The event dispatcher.
 */
REFEventReactor::REFEventReactor(REFEventDispatcher* pDispatcher)
	: REFServiceBase(pDispatcher)
{
	BFX_ASSERT(pDispatcher != NULL);
	m_pDispatcher = pDispatcher;

	m_lShutdown = 0;
	m_pInterrupter = new REFEventInterrupter();
}

REFEventReactor::~REFEventReactor()
{
}

void REFEventReactor::Shutdown()
{
#ifdef	BFX_EVENT_HAS_IOCP
	MutexHolder holder(&m_mutex);
	// stop the work thread.

	holder.Release();

	if (m_pThread != NULL)
	{
		m_pInterrupter->Interrupt();
		m_pThread->Join();
		m_pThread = NULL;
	}
#endif

	// TODO: more...
}

/**
 * Initializes the reactor for processing tasks.
 */
void REFEventReactor::Restart()
{
	m_pDispatcher->RestartReactor();
}

/**
 * Adds a new timer event queue into the reactor.
 * @param pTimerQueue the timer event queue to be add.
 */
void REFEventReactor::AddTimerQueue(REFTimerQueue* pTimerQueue)
{
	MutexHolder holder(&m_mutex);
	m_pTimerQueues->Insert(pTimerQueue);
}

/**
 * Removes the timer event queue from the reactor.
 * @param pTimerQueue the timer event queue to be remove.
 */
void REFEventReactor::RemoveTimerQueue(REFTimerQueue* pTimerQueue)
{
	m_pTimerQueues->Remove(pTimerQueue);
}

void REFEventReactor::ScheduleTimer(REFTimerQueue* pTimerQueue, REFTimerContext* pContext)
{
	BFX_REQUIRE1(pContext != NULL, "Null pointer of 'pContext' is disallowed.");

	MutexHolder lock(&m_mutex);

	if (m_lShutdown)
	{
		// The event reactor is already shutdown.
		m_pDispatcher->PostImmediateCompletion(pContext);
		return;
	}

	bool bResetRequired = pTimerQueue->EnqueueContext(pContext);
	m_pDispatcher->WorkStarted();
	if (bResetRequired)
		m_pInterrupter->Interrupt();
}

/**
 *
 * @param pTimerQueue
 * @param pTimer
 */
void REFEventReactor::CancelTimer(REFTimerQueue* pTimerQueue, REFTimer* pTimer)
{
	MutexHolder lock(&m_mutex);
#if	0
	REF<REFTimerContextQueue> pContexts = new REFTimerContextQueue();
	pTimerQueue->CancelTimer(pTimer, pContexts);
#endif	//	#if	0
}

/**
 * Run select once until interrupted or events are ready to be dispatched.
 *
 * @param[in] bBlocking The value that indicates whether the event-loop is in blocking mode.
 * @param[out] pContexts The queue to receive contexts will be completed.
 */
void REFEventReactor::DispatchEvents(bool bBlocking, REFEventContextList* pContexts)
{
	MutexHolder lock(&m_mutex);
#if	0
	// set up the descriptor sets.
	REFFileDescriptorSet fdSets[MAX_SELECT_EVENTS];
	SOCKET fdMax = 0;
	bool bNotingToDo = true;

	// install our interrupter.
	fdSets[READ_EVENT].Set(m_pInterrupter->GetFileDescriptor());

	for (int i = 0; i < MAX_SELECT_EVENTS; i ++)
	{
		fdSets[i].AddRef();	// NOTE: prevent external call attempts to delete on stack variables.

		bNotingToDo = (bNotingToDo && m_queue[i].IsEmpty());
		m_queue[i].GetFileDescriptors(&fdSets[i], pContexts);
		fdMax = __MAX(fdSets[i].GetMaxFileDescriptor(), fdMax);
	}

#if defined(_WIN32) || defined(__CYGWIN__)
	// connection operations on Windows use both except and write fd_sets.
	bNotingToDo = (bNotingToDo && m_queue[CONNECT_EVENT].IsEmpty());
	m_queue[CONNECT_EVENT].GetFileDescriptors(&fdSets[WRITE_EVENT], pContexts);
	fdMax = __MAX(fdSets[WRITE_EVENT].GetMaxFileDescriptor(), fdMax);
	m_queue[CONNECT_EVENT].GetFileDescriptors(&fdSets[EXCEPT_EVENT], pContexts);
	fdMax = __MAX(fdSets[EXCEPT_EVENT].GetMaxFileDescriptor(), fdMax);
#endif

	// we can return immediately if there's no work to do and the reactor is not supposed to block.
	if (!bBlocking && bNotingToDo)
		return;

	lock.Release();

	// determine how long to block while waiting for events.
	timeval timeout = { 0, 0 };
	if (bBlocking)
	{
		const int US = 5 * 60 * 1000000;	// we will wait no longer than 5 minutes
		timeout.tv_sec = US / 1000000;
		timeout.tv_usec = US % 1000000;
	}

	// block on the select call until at least one of descriptors become ready.
	int nRetVal = select(fdMax + 1, fdSets[READ_EVENT], fdSets[WRITE_EVENT], fdSets[EXCEPT_EVENT], &timeout);

	// reset our interrupter.
    if (nRetVal > 0 && fdSets[READ_EVENT].IsSet(m_pInterrupter->GetFileDescriptor()))
    	m_pInterrupter->Reset();

	lock.Acquire();

	// dispatch all ready operations.
	if (nRetVal > 0)
	{
#if defined(_WIN32) || defined(__CYGWIN__)
		// connection operations on Windows use both except and write fd_sets.
		m_queue[CONNECT_EVENT].PerformEvents(&fdSets[WRITE_EVENT], pContexts);
		m_queue[CONNECT_EVENT].PerformEvents(&fdSets[EXCEPT_EVENT], pContexts);
#endif
		// exception operations must be processed first to ensure that any out-of-band data is read before normal data.
		for (int i = MAX_SELECT_EVENTS - 1; i >= 0; --i)
			m_queue[i].PerformEvents(&fdSets[i], pContexts);
	}
#endif	//	#if	0
}

} /* namespace BFX */
